#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void *function_01() {
    int a = 100;

    int *p = &a;

    return p;
    // 函数局部变量的地址是不能返回的, 函数局部变量的内存在函数结束后就已经释放, 其他函数在操作 a 的地址就是内存污染
    // 其他函数想要正常操作 a 的地址, 可以将 a 提升为静态变量或全局变量, 改变变量的生命周期来实现对 a 地址的操作
}

void *function_02(int a) {
    int *p = &a;

    return p;
    // 函数的形参就是局部变量, 道理和返回局部变量的地址一样
}

void *function_03() {
    int *p = malloc(sizeof(int));

    return p;
    // malloc 函数申请的内存存在于堆区, 堆区的地址是可以正常返回的
}

void *function_04() {
    return malloc(sizeof(char) * 1024);
}

void function_04_failure() {
    char *string = function_04();
    // 错误的将堆内存的地址赋值
    // string = "Hello World!" 是将字符串常量的地址给 string, 当前操作并没有将堆区的内存值改为 Hello World!
    // 当前操作不仅会造成 malloc 申请内存的没有释放, 还造成后续 free 释放 string 内存出错
    // 因为当前 string 存储的是字符串常量的地址, 字符串常量的地址是不能释放的
    string = "Hello World!";
    printf("%s\n", string);
    free(string);
}

void function_04_success() {
    char *string = function_04();
    // 正确的将堆内存的地址赋值
    memcpy(string, "Hello World!", sizeof(char) * strlen("Hello World!"));
    printf("%s\n", string);
    printf("\n");
    free(string);
}

void function_05() {
    int length = 10;
    int *p1 = malloc(sizeof(int) * length);

    for (int i = 0; i < length; ++i) {
        printf("%d ", p1[i]);
    }
    printf("\n");

    // calloc 和 malloc 功能类似, calloc 会分配 n 块字节为 m 的连续内存
    // 并且 calloc 对分配内存会给定初始值 0, 而 malloc 分配内存的初始值为随机值
    int *p2 = calloc(length, sizeof(int));

    for (int i = 0; i < length; ++i) {
        printf("%d ", p2[i]);
    }
    printf("\n");
    printf("\n");
}

void function_06() {
    // realloc 可以给已经分配内存的地址重新设置新的内存大小

    char *string1 = malloc(sizeof(char) * 1024);

    memcpy(string1, "Hello World!", sizeof(char) * strlen("Hello World!"));
    printf("string1 = %s\n", string1);

    // 这时候你觉得 string1 的内存分配过多, 你可以给通过 realloc 给 string1 的内存设置小点的值
    // realloc 设置新值小于之前的内存值时, 返回的地址和之前一样的
    printf("string address : %p\n", string1);
    string1 = realloc(string1, 128);
    printf("string new address : %p\n", string1);

    // 这时候你又觉得 string1 的内存可能不够, 也可以使用 realloc 给 string1 的内存设置大点的值
    // realloc 设置新值大于之前的内存值时, 返回的地址可能和之前一样
    // 也可能是在新地方分配一个大点的内存, 然后在将之前的数据拷贝到新地方
    printf("string address : %p\n", string1);
    string1 = realloc(string1, 256);
    printf("string new address : %p\n", string1);
}

int main() {
    // function_04_failure();
    function_04_success();

    function_05();

    function_06();
}
